#ifndef __COMPLEX_HPP__
#define __COMPLEX_HPP__

#include "math/math_config.h"
#include <cmath>

NS_MATH_BEGIN

template<typename T>
class complex
{
public:
	typedef T value_type;
	typedef T& reference;
	typedef const T& const_reference;
	typedef complex<T> this_type;
	
public:
	// Data
	value_type real;
	value_type imag;

public:
	complex():real(0),imag(0){}
	complex(value_type r, value_type i=0):real(r),imag(i){}
	complex(const this_type& c):real(c.real),imag(c.imag){}
	~complex(){}

public:
	// Inline functions
	double len2()const{return (double)real*real+(double)imag*imag;}
	double len()const{return std::sqrt(len2());}
	this_type conj()const{return this_type(real, -imag);}
	double angle()const{return atan2((double)imag, (double)real);}
	value_type* ptr(){return data;}
	const value_type* ptr()const{return data;}

public:
	reference operator[] (int i){return *(&real+i);}
	const_reference operator[] (int i)const{return *(&real+i);}
	
public:
	// Operators
	this_type& operator= (const this_type& c){real = c.real; imag = c.imag; return *this;}
	this_type& operator= (const value_type& r){real = r; return *this;}

	this_type& operator+= (const this_type& c){real += c.real; imag += c.imag; return *this;}
	this_type& operator+= (const value_type& v){real += v; return *this;}
	this_type& operator-= (const this_type& c){real -= c.real; imag -= c.imag; return *this;}
	this_type& operator-= (const value_type& v){real -= v; return *this;}
	this_type& operator*= (const this_type& c)
	{
		value_type r, i;
		r = real*c.real - imag*c.imag;
		i = real*c.imag + imag*c.real;
		real = r;
		imag = i;
		return *this;
	}
	this_type& operator*= (const value_type& v)
	{
		real *= v;
		imag *= v;
		return *this;
	}
	this_type& operator/= (const this_type& c)
	{
		value_type r, i, l;
		l = len2();
		r = (real*c.real + imag*c.imag)/l;
		i = (imag*c.real - real*c.imag)/l;
		real = r;
		imag = i;
		return *this;
	}
	this_type& operator/= (const value_type& v)
	{
		real /= v;
		imag /= v;
		return *this;
	}

#define CPX_OP2(OP1, OP2, TYPE1) this_type operator OP1 (const TYPE1& c)const{this_type ret(*this); ret OP2 c; return ret;}

	CPX_OP2(+,+=,this_type);
	CPX_OP2(+,+=,value_type);
	CPX_OP2(-,-=,this_type);
	CPX_OP2(-,-=,value_type);
	CPX_OP2(*,*=,this_type);
	CPX_OP2(*,*=,value_type);
	CPX_OP2(/,/=,this_type);
	CPX_OP2(/,/=,value_type);

#undef CPX_OP2

	this_type operator- ()const{this_type ret(-real, -imag); return ret;}

};

// Inline Functions
template<typename T>
T real(const complex<T>& c){return c.real;}
template<typename T>
T imag(const complex<T>& c){return c.imag;}
template<typename T>
complex<T> conj(const complex<T>& c){return complex<T>(c.real, -c.imag);}
template<typename T>
double angle(const complex<T>& c){return c.angle();}
template<typename T>
double len(const complex<T>& c){return c.len();}
template<typename T>
double len2(const complex<T>& c){return c.len2();}

NS_MATH_END

#endif